﻿using Playnite.Common;
using Playnite.SDK;
using Playnite.SDK.Data;
using Playnite.SDK.Models;
using Playnite.SDK.Plugins;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace LocalGameLibrary
{
    public class LocalGameLibrary : LibraryPlugin
    {
        private LocalGameMetadataProbeManager gameProbeManager = new LocalGameMetadataProbeManager();
        private Dictionary<string, LocalGameMetadata> localGameMetadataDict = new Dictionary<string, LocalGameMetadata>();
        public Dictionary<string, LocalGameMetadata> LocalGameMetadataDict { get { return this.localGameMetadataDict; } }

        private static readonly ILogger logger = LogManager.GetLogger();

        private LocalGameLibrarySettingsViewModel settingsViewModel { get; set; }

        public override Guid Id { get; } = Guid.Parse("d8141747-c1a1-4236-a20f-45b83aea96c0");

        // Change to something more appropriate
        public override string Name => "LocalGameLibrary";

        // Implementing Client adds ability to open it via special menu in playnite.
        public override LibraryClient Client { get; } = new LocalGameLibraryClient();

        public LocalGameLibrary(IPlayniteAPI api) : base(api)
        {
            settingsViewModel = new LocalGameLibrarySettingsViewModel(this);
            Properties = new LibraryPluginProperties
            {
                HasSettings = true
            };
            this.InitializeLocalGameMetadatas();
        }

        public void InitializeLocalGameMetadatas()
        {
            try
            {
                var localGameMetadataCollection = Serialization.FromJsonFile<LocalGameMetadataCollection>(settingsViewModel.Settings.MetadataCollectionCacheFilePath); // D:\projects\kp-playnite\PlayniteExtensions-JimmyMo\LocalGameMapping.json"
                foreach (var localGameMetadata in localGameMetadataCollection)
                {
                    this.localGameMetadataDict[localGameMetadata.PID] = localGameMetadata;
                }
            }
            catch (Exception ex)
            {
                PlayniteApi.Notifications.Add(new NotificationMessage(
                    "ReadLocalGameMappings",
                    ex.Message,
                    NotificationType.Error,
                    () => OpenSettingsView()));
            }
        }
        //
        // Parameters:
        //   args:
        public override IEnumerable<PlayController> GetPlayActions(GetPlayActionsArgs args)
        {
            if (args.Game.PluginId != this.Id)
            {
                yield break;
            }
            var localGameMetadata = this.LocalGameMetadataDict[args.Game.GetPid()];
            if (!String.IsNullOrEmpty(localGameMetadata.PlayExec))
            {
                yield return new AutomaticPlayController(args.Game)
                {
                    Type = AutomaticPlayActionType.File,
                    TrackingMode = TrackingMode.Process,
                    //Arguments = task.Arguments,
                    Path = Path.Combine(args.Game.InstallDirectory, localGameMetadata.PlayExec),
                    WorkingDir = args.Game.InstallDirectory,
                    Name = args.Game.Name
                };
            }
            else
            {
                //ProcessStarter.StartProcess("explorer.exe " + args.Game.InstallDirectory);
                System.Diagnostics.Process.Start("Explorer.exe", args.Game.InstallDirectory);
                //this.PlayniteApi.Notifications.Add(new NotificationMessage("PlayExecNotSet", "Game PlayExec not set!", NotificationType.Info));
                yield break;
            }
        }

        //
        // Parameters:
        //   args:
        public override IEnumerable<InstallController> GetInstallActions(GetInstallActionsArgs args)
        {
            if (args.Game.PluginId != this.Id)
            {
                yield break;
            }
            yield return new LocalGameController(args.Game, this);
        }

        public LocalGameMetadataCollection ScanGameRepos()
        {
            bool reProbe = settingsViewModel.Settings.ReProbe;
            bool writeMetadataToRepo = settingsViewModel.Settings.WriteMetadataToRepo; // 某些情况比如远端Repo是只读权限，则不能将metadata写入Repo
            var repoRoots = settingsViewModel.Settings.RepoRoots;
            LocalGameMetadataCollection localGameMetadataCollection = new LocalGameMetadataCollection();
            foreach (var repoRoot in repoRoots)
            {
                if (Directory.Exists(repoRoot))
                {
                    foreach (var gameRepoFolderFullPath in Directory.GetDirectories(repoRoot))
                    {
                        var gameFolderName = Utils.GetLastFolderName(gameRepoFolderFullPath);
                        if (gameFolderName == "#recycle") continue;
                        LocalGameMetadata localGameMetadata;
                        
                        string metadataFilePath = Path.Combine(gameRepoFolderFullPath, @"LocalGameMetadata.json");
                        if (reProbe || !File.Exists(metadataFilePath))
                        {
                            localGameMetadata = gameProbeManager.Probe(gameRepoFolderFullPath);
                        }
                        else
                        {
                            localGameMetadata = Serialization.FromJsonFile<LocalGameMetadata>(metadataFilePath);
                            localGameMetadata.PID = gameFolderName;
                            localGameMetadata.RepoPath = gameRepoFolderFullPath;
                        }
                        localGameMetadataCollection.Add(localGameMetadata);
                        // 3. 如需要，则写入metadata至Repo
                        if (writeMetadataToRepo)
                        {
                            if (reProbe || !File.Exists(metadataFilePath))
                            {
                                Utils.WriteJsonFile(Path.Combine(localGameMetadata.RepoPath, @"LocalGameMetadata.json"), localGameMetadata);
                            }
                        }
                    }
                }
                else
                {
                    PlayniteApi.Notifications.Add(new NotificationMessage("RepoRootPathNotExists", $"RepoRootPath: {repoRoot} not exists!", NotificationType.Info));
                }
            }
            return localGameMetadataCollection;
        }

        public override IEnumerable<GameMetadata> GetGames(LibraryGetGamesArgs args)
        {
            bool requireScanRepo = settingsViewModel.Settings.RequireScanRepo;
            if (requireScanRepo)
            {
                LocalGameMetadataCollection localGameMetadataCollection = ScanGameRepos();
                Utils.WriteJsonFile(settingsViewModel.Settings.MetadataCollectionCacheFilePath, localGameMetadataCollection);
            }
            this.InitializeLocalGameMetadatas();

            foreach (var game in PlayniteApi.Database.Games)
            {
                //game.Notes += " testing";
                string pid = game.GetPid();
                if (!string.IsNullOrEmpty(pid) && this.localGameMetadataDict.ContainsKey(pid))
                {
                    //this.localGameMetadataDict[pid].RecommendedInstallFolderName;
                    if (!Regex.IsMatch(game.Notes, @"InstallFolderName: (.*)[$|\r|\n]?"))
                    {
                        string notesInstallFolderName = $"InstallFolderName: {this.localGameMetadataDict[pid].RecommendedInstallFolderName}";
                        game.Notes += System.Environment.NewLine + notesInstallFolderName;
                        PlayniteApi.Database.Games.Update(game);
                    }
                    if (game.IsInstalled && string.IsNullOrEmpty(game.InstallDirectory))
                    {
                        game.IsInstalled = false;
                        PlayniteApi.Database.Games.Update(game);
                    }
                    if (game.PluginId == Guid.Empty)
                    {
                        game.PluginId = this.Id;
                        PlayniteApi.Database.Games.Update(game);
                    }
                    //game.SourceId = new Guid("1ada14a7-4c5f-45c8-8b17-1682e959591b");
                    //PlayniteApi.Database.Games.Update(game);
                }
                else
                {
                    // JM：安装包在硬盘中不存在，或者更大可能就是PID写错了
                    PlayniteApi.Notifications.Add(new NotificationMessage($"PIDERROR_{game.Name}", $"Game {game.Name} PID is incorrect.", NotificationType.Info));
                    logger.Warn($"Game {game.Name} PID is incorrect.");
                }
                
                
                //if (string.IsNullOrEmpty(game.InstallDirectory))
                //{
                //    game.IsInstalled = false;
                //    PlayniteApi.Database.Games.Update(game);
                //    continue;
                //}

                //var dir = new DirectoryInfo(game.InstallDirectory);
                //if (!dir.Exists || !dir.EnumerateFileSystemInfos().Any())
                //{
                //    game.IsInstalled = false;
                //    PlayniteApi.Database.Games.Update(game);
                //    continue;
                //}
            }

            return null;
        }

        public override LibraryMetadataProvider GetMetadataDownloader()
        {
            return null;
        }

        /// <summary>
        /// 扫描游戏安装目录即LibraryRoot，认定为已安装的游戏满足如下条件
        /// 1. 游戏目录已存在
        /// 2. 游戏目录中存在metadata.json文件
        /// 3. metadata.json文件中明确定义了PlayExec，但是目录中找不到PlayExec（即游戏的执行程序还未安装完成）
        /// 4. 但是当metadata.json文件中么有明确定义PlayExec，一般为首次安装，不太清楚游戏执行程序名是什么，这种情况就会跳过第三条
        /// </summary>
        /// <returns></returns>
        internal Dictionary<string, InstalledGameMetadata> GetInstalledGames()
        {
            var games = new Dictionary<string, InstalledGameMetadata>();
            var libRoot = this.settingsViewModel.Settings.LibraryRoot;
            if (Directory.Exists(libRoot))
            {
                foreach (var gameFolderFullPath in Directory.GetDirectories(libRoot))
                {
                    //var folderName = Path.GetFileNameWithoutExtension(gameFolder);
                    var gameFolderName = Utils.GetLastFolderName(gameFolderFullPath);
                    var metadataFilePath = Directory.GetFiles(gameFolderFullPath, @"InstalledMetadata.json").FirstOrDefault<string>();
                    if (String.IsNullOrEmpty(metadataFilePath))
                    {
                        continue;
                    }
                    // 读安装后的游戏metadata
                    try
                    {
                        InstalledGameMetadata installedGameMetadata = Serialization.FromJsonFile<InstalledGameMetadata>(metadataFilePath);
                        //LocalGameMetadata metadata = Serialization.FromJsonFile<LocalGameMetadata>(metadataFilePath);
                        string playExec = this.LocalGameMetadataDict[installedGameMetadata.PID].PlayExec;
                        if (!String.IsNullOrEmpty(playExec) && !File.Exists(Path.Combine(gameFolderFullPath, playExec)))
                        { // PlayExec已定义 并且 PlayExec不存在
                            continue;
                        }

                        games.Add(installedGameMetadata.PID, installedGameMetadata);
                    } catch (Exception ex)
                    {
                        PlayniteApi.Notifications.Add(new NotificationMessage("ReadInstalledGameMetadataError", $"GetInstalledGame: {gameFolderName} {ex.Message}", NotificationType.Error));
                        continue;
                    }
                }
            }
            return games;
        }

        public override ISettings GetSettings(bool firstRunSettings)
        {
            return settingsViewModel;
        }

        public override UserControl GetSettingsView(bool firstRunSettings)
        {
            return new LocalGameLibrarySettingsView();
        }
    }
}